package com.like.banzipao.server.plugins

import com.like.banzipao.server.model.Msg
import com.like.banzipao.server.model.MsgType
import com.like.banzipao.server.model.Room
import com.like.banzipao.server.util.objectMapper
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import io.ktor.server.websocket.pingPeriod
import io.ktor.server.websocket.timeout
import io.ktor.server.websocket.webSocket
import io.ktor.websocket.CloseReason
import io.ktor.websocket.DefaultWebSocketSession
import io.ktor.websocket.Frame
import io.ktor.websocket.close
import io.ktor.websocket.readText
import java.time.Duration
import java.util.Collections

// 我们可以使用 Set 来保存打开的连接列表。可以使用一个普通的 try……finally 来跟踪它们。
// 由于 Ktor 默认是多线程的，因此我们应该使用线程安全的集合或者以 newSingleThreadContext 将代码体限制为单线程。
private val clients = Collections.synchronizedSet(LinkedHashSet<ChatClient>())

suspend fun broadcast(msg: Msg<*>) {
    val content = msg.content
    if (content is Room) {
        val roomClients = clients.filter {
            content.manager.contains(it.userId)// 本房间的在线用户
        }
        // 设置是否在线
        val onLineUserIds = roomClients.map { it.userId }
        content.players.forEach {
            it?.let {
                it.online = onLineUserIds.contains(it.user.id)
            }
        }
        // 发送消息给本房间的在线用户
        val msgStr = objectMapper.writeValueAsString(msg)
        println("broadcast $msgStr")
        for (client in roomClients) {
            client.session.outgoing.send(Frame.Text(msgStr))
        }
    } else if (content is String) {// 发给所有在线用户
        val msgStr = objectMapper.writeValueAsString(msg)
        println("broadcast $msgStr")
        for (client in clients) {
            client.session.outgoing.send(Frame.Text(msgStr))
        }
    }
}

fun Application.configureSockets() {
    install(WebSockets) {
        pingPeriod = Duration.ofSeconds(15)
        timeout = Duration.ofSeconds(15)
        maxFrameSize = Long.MAX_VALUE
        masking = false
    }
    routing {
        webSocket("/ws/{userId}") { // websocketSession
            val userId = call.parameters["userId"]?.toIntOrNull()
            if (userId == null) {
                close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "userId is null"))
                return@webSocket
            }
            val client = ChatClient(userId, this)
            clients += client
            // 如果当前用户已经在房间中，则广播进入房间消息
            getRoom(userId)?.let {
                broadcast(Msg(MsgType.ROOM, it))
            }
            try {
                while (true) {
                    when (val frame = incoming.receive()) {
                        is Frame.Text -> {
                            val text = frame.readText()
                            if (text.equals("bye", ignoreCase = true)) {
                                close(CloseReason(CloseReason.Codes.NORMAL, "Client said BYE"))
                            }
                        }

                        else -> {}
                    }
                }
            } finally {
                clients -= client
            }
        }
    }
}

class ChatClient(val userId: Int, val session: DefaultWebSocketSession)
